package com.dferreira.languagesteach.data_adapters;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.text.format.Formatter;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.dferreira.languagesteach.LanguagesTeachApplication;
import com.dferreira.languagesteach.R;
import com.dferreira.languagesteach.activities.generic.IFragmentStack;
import com.dferreira.languagesteach.constants.Constants;
import com.dferreira.languagesteach.custom_items.UITableViewCell;
import com.dferreira.languagesteach.data_adapters.generic.NavigationAdapter;
import com.dferreira.languagesteach.data_models.Language;
import com.dferreira.languagesteach.data_parsers.XMLLanguagesParser;
import com.dferreira.languagesteach.fragments.LessonsFragment;
import com.dferreira.languagesteach.utils.DataPaths;
import com.dferreira.repository.Repository;
import com.dferreira.repository.data_models.PackageMetadata;
import com.dferreira.repository.services.RepositoryNotifier;
import com.dferreira.repository.services.RepositoryReceiver;

import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Iterator;
import java.util.Locale;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

/**
 * Do the parse from languages.xml and create an array list from items with this
 * items create a listView where the user can choose what language he want to
 * learn
 * <p/>
 * Currently used in languages screen
 *
 * @author dferreira
 */
public class LanguagesAdapter extends NavigationAdapter {

    private static String TAG = "LanguagesAdapter";
    private AlertDialog questionDialog;
    private AlertDialog warnDialog;
    private Repository packagesRepository;
    private RepositoryReceiver repositoryReceiver;

    private long lastClick;


    /**
     * @param context                reference to the which the dataSource belongs
     * @param fragmentStack
     * @param packageInstalledFolder
     */
    public LanguagesAdapter(Context context, IFragmentStack fragmentStack, String packageInstalledFolder) {
        super(context, packageInstalledFolder);
        this.setFragmentStack(fragmentStack);
    }

    /**
     * Build the dataSet and puts the result in instance variables
     *
     * @return false -> An error
     * true -> Everything worked OK.
     */
    public boolean buildDataSet() {
        boolean couldBuildTheList;

        // Local variables
        Iterator<Language> languages;

        languages = ParseLanguagesXML();
        if (languages == null) {
            return false;
        }
        couldBuildTheList = buildLanguagesList(languages);
        if (couldBuildTheList) {
            dataSetWellConstructed();
        }
        return couldBuildTheList;
    }

    /**
     *
     */
    public void updateLanguagesInstallProgress() {
        for (int i = 0; i < getCount(); i++) {
            UITableViewCell cell = getItem(i);
            cell.setProgress(packagesRepository.getInstallationProgress(cell.getTag()));
        }
    }

    /**
     * Prepare for a progress bar dialog
     *
     * @param packageName name from the package to install
     */
    private void installLanguage(String packageName) {
        packagesRepository.installPackage(packageName);
    }

    /**
     * Allow the user to choose if want install some language package
     *
     * @param packageName   label from the language to show
     * @param sizeToInstall in bytes that the package will require
     */
    private void showInstallPackageOption(final String packageName,
                                          long sizeToInstall) {
        AlertDialog.Builder builder;

        String sizeToInstallStr;
        String installQuestion;
        StringBuilder strLang;
        final String displayLanguage;
        Locale locale;


        builder = new AlertDialog.Builder(getContext());

        sizeToInstallStr = Formatter.formatFileSize(getContext(), sizeToInstall);
        locale = new Locale(packageName);

        strLang = new StringBuilder(locale.getDisplayLanguage());
        strLang.setCharAt(0, Character.toUpperCase(strLang.charAt(0)));
        displayLanguage = strLang.toString();
        installQuestion = getContext().getString(R.string.install_question_pattern,
                displayLanguage, sizeToInstallStr);

        builder.setMessage(installQuestion);
        builder.setCancelable(true);
        builder.setPositiveButton(getContext().getString(R.string.answer_yes),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        installLanguage(packageName);
                        dialog.cancel();
                    }
                }
        );
        builder.setNegativeButton(getContext().getString(R.string.answer_no),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                }
        );
        questionDialog = builder.create();
        questionDialog.show();
    }

    /**
     * Show a message to the user explaining that he can not install the desire
     * language because does not have internet connection
     *
     * @param v view with the language that user is trying to install
     */
    private void showNoInternetConnection(final View v) {
        AlertDialog.Builder builder;
        TextView textLabel;
        String packageLabel;
        CharSequence packageText;
        String msg;

        builder = new AlertDialog.Builder(getContext());

        textLabel = (TextView) v.findViewById(R.id.textLabel);

        if (textLabel != null) {
            packageText = textLabel.getText();
            if (packageText != null) {
                packageLabel = packageText.toString();
            } else {
                return;
            }

        } else {
            return;
        }
        msg = getContext().getString(R.string.no_internet_connection, packageLabel);
        builder.setMessage(msg);
        builder.setPositiveButton(getContext().getString(R.string.ok_button),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                }
        );
        warnDialog = builder.create();
        warnDialog.show();
    }

    /**
     * Tells to user that is don't has enough space to install the language
     *
     * @param packageName   label from the language to show
     * @param sizeToInstall size that would be required to install the package
     */
    private void showNoEnoughSpace(final String packageName,
                                   long sizeToInstall) {
        AlertDialog.Builder builder;
        String msg;
        String sizeToInstallStr;
        StringBuilder strLang;
        final String displayLanguage;
        Locale locale;


        sizeToInstallStr = Formatter.formatFileSize(getContext(), sizeToInstall);
        locale = new Locale(packageName);

        strLang = new StringBuilder(locale.getDisplayLanguage());
        strLang.setCharAt(0, Character.toUpperCase(strLang.charAt(0)));
        displayLanguage = strLang.toString();
        msg = getContext().getString(R.string.without_enough_space,
                displayLanguage, sizeToInstallStr);


        builder = new AlertDialog.Builder(getContext());


        builder.setMessage(msg);
        builder.setPositiveButton(getContext().getString(R.string.ok_button),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                }
        );
        warnDialog = builder.create();
        warnDialog.show();
    }

    /**
     * Tells to the user wait a moment
     */
    private void showToastWait() {
        String updatingRepositoryMsg;
        Toast toast;

        updatingRepositoryMsg = getContext()
                .getString(R.string.updating_repository_msg);
        toast = Toast.makeText(getContext(), updatingRepositoryMsg,
                Toast.LENGTH_LONG);
        toast.setGravity(Gravity.CENTER, 0, 0);
        toast.show();
    }

    /**
     * Parse the languages.xml file
     *
     * @return iterator over language dataModel
     */
    private Iterator<Language> ParseLanguagesXML() {
        // Local variables
        SAXParserFactory spf;
        SAXParser sp;
        XMLReader xr;
        XMLLanguagesParser xmlLanguagesParser;
        FileInputStream inputStream;
        File file;
        String languagesFileName;

        languagesFileName = Constants.LANGUAGES_FILE_NAME;
        try {
            // parsing from XML file
            spf = SAXParserFactory.newInstance();
            sp = spf.newSAXParser();
            xr = sp.getXMLReader();
            xmlLanguagesParser = new XMLLanguagesParser();
            xr.setContentHandler(xmlLanguagesParser);
            file = new File(getPackageInstalledFolder() + File.separator
                    + languagesFileName);
            inputStream = new FileInputStream(file);
            xr.parse(new InputSource(inputStream));
            inputStream.close();

            return xmlLanguagesParser.getLanguages().iterator();
        } catch (FileNotFoundException e) {
            Log.e(TAG, e.getMessage());
            return null;
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
            return null;
        }
    }

    /**
     * Build the list of items based on languages which have get
     *
     * @param languages set of languages how have been read
     * @return false   -> some error happened
     * true    -> everything was just fine
     */
    private boolean buildLanguagesList(Iterator<Language> languages) {
        String flagsFolder;
        String iconsExtension;
        boolean couldReadTheFlag;
        Locale locale;
        StringBuilder strLang;

        while (languages.hasNext()) {
            UITableViewCell cell;

            flagsFolder = Constants.FLAGS_FOLDER;
            iconsExtension = Constants.ICONS_EXTENSION;

            Language language = languages.next();
            cell = new UITableViewCell();

            locale = new Locale(language.getPrefix());
            strLang = new StringBuilder(locale.getDisplayLanguage());
            strLang.setCharAt(0, Character.toUpperCase(strLang.charAt(0)));

            cell.setTextLabel(strLang.toString());
            cell.setTag(language.getPrefix());
            couldReadTheFlag = cell.setImageView(getPackageInstalledFolder()
                    + File.separator + flagsFolder + File.separator
                    + language.getPrefix() + iconsExtension);
            if (!couldReadTheFlag) {
                return false;
            }
            add(cell);
        }
        return true;
    }


    /**
     * Put the levels control to the canvas
     */
    private void pushLessonsFragment(View v) {
        DataPaths dataPaths;
        PackageMetadata pkgMetadata;
        long sizeToInstall;


        dataPaths = new DataPaths(getContext());
        if (packagesRepository.isPackageInstalled(LanguagesTeachApplication.getApplicationState().getLanguagePrefix())) {
            // Package is already installed
            pkgMetadata = packagesRepository.getInstalledPackageInfo(LanguagesTeachApplication.getApplicationState().getLanguagePrefix());

            LessonsFragment lessonsFragment = new LessonsFragment();
            lessonsFragment.setArguments(pkgMetadata.getInstalledPath(), getFragmentStack());
            getFragmentStack().pushFragment(lessonsFragment);
        } else {
            if (packagesRepository.packageNeedsToBeRecovered(LanguagesTeachApplication.getApplicationState().getLanguagePrefix())) {
                packagesRepository.uninstallDamagedPkg(LanguagesTeachApplication.getApplicationState().getLanguagePrefix());
            }
            sizeToInstall = packagesRepository.getInstallBudget(LanguagesTeachApplication.getApplicationState().getLanguagePrefix());

            if (dataPaths.isNetworkAvailable()) {
                if (sizeToInstall == -1) {
                    sizeToInstall = packagesRepository.getInstallBudget(LanguagesTeachApplication.getApplicationState()
                            .getLanguagePrefix());
                    if (sizeToInstall == -1) {
                        if (packagesRepository.updateRepositoryAlreadyFinish()) {
                            showNoInternetConnection(v);
                        } else {
                            showToastWait();
                        }
                    }
                } else {
                    if (dataPaths.getAvailableSpaceFromPackagesPath() < sizeToInstall) {
                        showNoEnoughSpace(LanguagesTeachApplication.getApplicationState().getLanguagePrefix(),
                                sizeToInstall);
                    } else {
                        showInstallPackageOption(LanguagesTeachApplication.getApplicationState().getLanguagePrefix(),
                                sizeToInstall);
                    }
                }
            } else {
                showNoInternetConnection(v);
            }
        }
    }

    /**
     * Called when the user click at on of the languages available
     *
     * @param v view that was clicked
     */
    @Override
    public synchronized void onClick(View v) {
        Integer minIntervalClick;

        minIntervalClick = Constants.MIN_INTERVAL_CLICK;
        long dtMilli = System.currentTimeMillis();
        if (dtMilli < (lastClick + minIntervalClick)) {
            return;
        }
        lastClick = dtMilli;

        LanguagesTeachApplication.getApplicationState().setLanguagePrefix((String) v.getTag());

        pushLessonsFragment(v);

    }


    /**
     * This function allows to un-install the last package viewed
     */
    public void uninstallLastLanguageViewed() {
        packagesRepository.uninstallDamagedPkg(LanguagesTeachApplication.getApplicationState().getLanguagePrefix());
    }

    /**
     * Called when main activity resumes his activity
     */
    public void onResume() {
        this.packagesRepository = new Repository(getContext());
        this.repositoryReceiver = new RepositoryReceiver(this);

        IntentFilter intentFilter = new IntentFilter(RepositoryNotifier.BROADCAST_ACTION);
        intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
        LocalBroadcastManager.getInstance(getContext()).registerReceiver(repositoryReceiver, intentFilter);

        updateLanguagesInstallProgress();

    }

    /**
     * Called when main activity enters in pause
     */
    public void onPause() {

        LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(repositoryReceiver);

        if ((questionDialog != null) && (questionDialog.isShowing())) {
            questionDialog.dismiss();
        }
        if ((warnDialog != null) && (warnDialog.isShowing())) {
            warnDialog.dismiss();
        }
    }
}
